/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

// 草图 - 椭圆

import { KEYBOARD_KEY } from '@app-cad/common/Constants';
import { wsApi } from '@app-cad/server/wsApi';
import i18n from 'i18next';
import { useEffect, useState } from 'react';
import { EllipseCurve, Geometry, Vector3 } from 'three-legacy';

import {
  BaseCommand,
  CENTEREVENTS,
  CSCircle,
  CSLine,
  CSys,
  CadScene,
  CommandHint,
  CommandHintStatus,
  MathTool,
  SceneGraph,
  cadAppErrorLog,
  css2dInputLable,
  useStore,
} from '@amcax/base';
import { Picker3D, PickerMgr } from '@amcax/renderer';
import { eventCenterManager } from '@app-base/common/events/EventCenterManager';
import { ctx2 } from '@app-cad/module';

enum State {
  NONE,
  DRAWCIRCLE,
  DRAWELLIPSE,
  FINISH,
}

type MouseState =
  | State.NONE
  | State.DRAWCIRCLE
  | State.DRAWELLIPSE
  | State.FINISH;

interface TrackingData {
  circle: CSCircle;
  ellipse: CSLine;
  rLine: CSLine;
  rLabel: css2dInputLable;
}

interface CircleCommandProps {
  title: string;
  onClose: () => void;
}

function CircleCommand({ onClose }: CircleCommandProps) {
  const updateCmdHighlightId = useStore((state) => state.updateCmdHighlightId);
  const [hits, setHits] = useState<string>('');

  const cadScene = ctx2.cadScene as CadScene;
  const sceneSetup = ctx2.viewer.sceneSetup;
  const assisGroup = cadScene.assisGroup;

  const xAxis = new Vector3(1, 0, 0);
  const yAxis = new Vector3(0, 1, 0);
  const zAxis = new Vector3(0, 0, 1);
  type MousePoint = {
    x: number;
    y: number;
  };

  // 默认为 XOY 平面
  let faceCSys: CSys = MathTool.createCsysFromPlacement([
    [0, 0, 0],
    [0, 0, 1],
    [1, 0, 0],
  ]);
  let planeNormal: Vector3 = faceCSys.getNormalVc3();
  let planeOrigin: Vector3 = faceCSys.getOriginVc3();
  let planeName: string = 'XOY';

  let startPoint: Vector3; // 绘制起点：圆心
  let endPoint: Vector3; // 绘制终点 ：圆上的某个点
  // let startPoint2D: Vector2;
  // let endPoint2D: Vector2;
  let xRadius: number = 0; // 椭圆Xradius
  let yRadius: number = 0; //  椭圆Yradius
  const angle: number = 0; // 椭圆在X轴正方向逆时针的旋转角度
  let mouseVW: Vector3;
  let mousePoint: MousePoint = {
    x: 0,
    y: 0,
  };

  let currentStatus: MouseState = State.NONE; // 记录当前状态

  const trackingData: TrackingData = {
    circle: null,
    ellipse: null,
    rLine: null,
    rLabel: null,
  };

  useEffect(() => {
    init();

    return onDestroy;
  }, []);

  const init = () => {
    registerEvents();
    changePlaneToXOY();
  };
  // ========= 鼠标和键盘事件=========
  const canvas = ctx2.container;

  const registerEvents = () => {
    document.addEventListener('keyup', onKeyup);
    canvas.addEventListener('mouseup', onMouseClick);
    // ctx2.on(EVENTS.MOUSE_SYS_MOVE, onMouseMove)
    eventCenterManager.activeEventCenter.on(
      CENTEREVENTS.POINTERMOVE,
      onMouseMove,
    );
  };
  const onKeyup = (e: KeyboardEvent) => {
    if (e.key === KEYBOARD_KEY.ESC) {
      onDestroy();
    } else if (e.key === KEYBOARD_KEY.ENTER) {
      if (currentStatus === State.DRAWCIRCLE) {
        finishDraw();
        onDestroy();
      }
    } else if (e.key === KEYBOARD_KEY.ARROWUP) {
      changePlaneToXOY();
    } else if (e.key === KEYBOARD_KEY.ARROWRIGHT) {
      changePlaneToYOZ();
    } else if (e.key === KEYBOARD_KEY.ARROWLEFT) {
      changePlaneToXOZ();
    } else if (e.key === KEYBOARD_KEY.ARROWDOWN) {
      changePlaneToXOY();
    }
  };
  const onMouseClick = (e: any) => {
    if (e.button === 0) {
      if (currentStatus === State.NONE) {
        startPoint = mouseVW;
        planeOrigin = startPoint.clone();
        createFaceCSys();
        trackingData.circle = null;
        currentStatus = State.DRAWCIRCLE;
      } else if (currentStatus === State.DRAWCIRCLE) {
        currentStatus = State.DRAWELLIPSE;
        if (trackingData.circle) {
          trackingData.circle.destroy();
        }
      } else if (currentStatus === State.DRAWELLIPSE) {
        currentStatus = State.FINISH;
        // 鼠标右键，完成
        finishDraw();
        onDestroy();
      }
    } else if (e.button === 2) {
      currentStatus = State.FINISH;
      // 鼠标右键，放弃
      onDestroy();
    }
  };
  const onMouseMove = (e: any) => {
    const pickRes = PickerMgr.getPicker<Picker3D>('3d').pick(e, { face: true });
    if (pickRes && pickRes?.assisObj?.csys) {
      planeNormal = pickRes.assisObj.csys?.getNormalVc3();
      planeOrigin = pickRes.assisObj.csys?.getOriginVc3();
    }
    mousePoint.x = e.event.layerX;
    mousePoint.y = e.event.layerY;
    mouseVW = getMouseWorldVector(mousePoint); // 当前鼠标的世界坐标
    if (currentStatus === State.DRAWCIRCLE) {
      endPoint = mouseVW.clone();
      xRadius = calcRadius(startPoint.clone(), endPoint.clone());
      preview();
    } else if (currentStatus === State.DRAWELLIPSE) {
      endPoint = mouseVW.clone();
      yRadius = calcRadius(startPoint.clone(), endPoint.clone());
      if (faceCSys) {
        // startPoint2D = new Vector2().fromArray(
        //   faceCSys.inTransformation.apply(startPoint.clone()).toArray(),
        // );
        // endPoint2D = new Vector2().fromArray(
        //   faceCSys.inTransformation.apply(endPoint.clone()).toArray(),
        // );
      } else {
        cadAppErrorLog('error: faceCSys为空');
      }
      preview();
    }
  };
  // ========= 鼠标和键盘事件 =========
  // ========= 位置变换 =========
  const changePlaneToXOY = () => {
    changePlane(zAxis, 'XOY');
  };
  const changePlaneToYOZ = () => {
    changePlane(xAxis, 'YOZ');
  };
  const changePlaneToXOZ = () => {
    changePlane(yAxis, 'XOZ');
  };
  const changePlane = (_axis: Vector3, _planeName: string) => {
    planeName = _planeName;
    planeNormal = _axis.clone();
    createFaceCSys();
    preview();
    setHits(
      '点击确定圆心，当前绘制平面为 ' +
        planeName +
        ' 平面，按方向键切换绘制平面',
    );
  };

  const createFaceCSys = () => {
    let _xAxis = []; // 局部坐标系的X轴
    let _zAxis = []; // 局部坐标系的Z轴
    if (planeName === 'XOY') {
      _xAxis = [1, 0, 0];
      _zAxis = zAxis.clone().toArray();
    } else if (planeName === 'YOZ') {
      _xAxis = [0, 0, 1];
      _zAxis = xAxis.clone().toArray();
    } else if (planeName === 'XOZ') {
      _xAxis = [1, 0, 0];
      _zAxis = yAxis.clone().toArray();
    }
    const placement = [planeOrigin.clone().toArray(), _zAxis, _xAxis];
    faceCSys = MathTool.createCsysFromPlacement(placement);
    trackingData.circle && trackingData.circle.resetCsys(faceCSys);
    trackingData.rLine && trackingData.rLine.resetCsys(faceCSys);
    endPoint = getMouseWorldVector(mousePoint);
  };

  // 计算半径
  const calcRadius = (start: Vector3, end: Vector3): number => {
    return start.clone().distanceTo(end.clone());
  };
  const getMouseWorldVector = (mousePoint: MousePoint): Vector3 => {
    const normal = planeNormal.clone();
    const origin = planeOrigin.clone();
    const mouseWvec = sceneSetup.mouseOnPlane(
      mousePoint.x,
      mousePoint.y,
      normal,
      origin,
    );
    return mouseWvec;
  };
  // ========= 位置变换 =========
  // ========= 释放资源 =========
  const onDestroy = () => {
    faceCSys = null;
    mouseVW = null;
    mousePoint = {
      x: 0,
      y: 0,
    };
    // setPickMode(['shell'])
    unregisterEvents();
    releaseObjects();
    onClose();
  };
  const unregisterEvents = () => {
    document.removeEventListener('keyup', onKeyup);
    canvas.removeEventListener('mouseup', onMouseClick);

    eventCenterManager.activeEventCenter
      .off(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .addExit(onDestroy);
  };
  const releaseObjects = () => {
    trackingData.circle && trackingData.circle.destroy();
    trackingData.rLine && trackingData.rLine.destroy();
    trackingData.rLabel && trackingData.rLabel.destroy();

    assisGroup.clear();
    SceneGraph.emptyGroup(assisGroup);
    ctx2.viewer.requestRender();

    updateCmdHighlightId('');
  };
  // ========= 释放资源 =========

  const finishDraw = async () => {
    if (xRadius != 0) {
      let axis: number[];

      if (planeName === 'XOY') {
        if (yRadius > xRadius) {
          axis = [0, 1, 0];
        } else {
          axis = [1, 0, 0];
        }
      } else if (planeName === 'YOZ') {
        if (yRadius > xRadius) {
          axis = [0, 1, 0];
        } else {
          axis = [0, 0, 1];
        }
      } else if (planeName === 'XOZ') {
        if (yRadius > xRadius) {
          axis = [0, 0, 1];
        } else {
          axis = [1, 0, 0];
        }
      }

      await wsApi.createEllipse(startPoint, xRadius, xRadius / 2, axis, [
        planeNormal.x,
        planeNormal.y,
        planeNormal.z,
      ]);
    }
  };

  const preview = () => {
    if (currentStatus === State.DRAWCIRCLE) {
      // 绘制圆
      if (!trackingData.circle) {
        trackingData.circle = new CSCircle({
          center: startPoint,
          radius: xRadius,
          csys: faceCSys,
          lineWidth: 4,
        });
        trackingData.circle.addTo(assisGroup);
      } else {
        trackingData.circle.update({ radius: xRadius });
      }

      // 绘制圆半径辅助线
      if (!trackingData.rLine) {
        trackingData.rLine = new CSLine({
          vertexs: [startPoint, endPoint],
          dashed: true,
          lineWidth: 2,
          depthTest: true,
          csys: faceCSys,
        });
        trackingData.rLine.addTo(assisGroup);
      } else {
        // trackingData.rLine.setVertexs([startPoint, endPoint])
        trackingData.rLine.update({
          vertexs: [startPoint, endPoint],
        });
      }

      // draw input label
      const labelText = startPoint.distanceTo(endPoint);
      const labelPos = new Vector3();
      labelPos.x = (startPoint.x + endPoint.x) / 2;
      labelPos.y = (startPoint.y + endPoint.y) / 2;
      labelPos.z = (startPoint.z + endPoint.z) / 2;
      if (!trackingData.rLabel) {
        trackingData.rLabel = new css2dInputLable({
          text: labelText,
          position: labelPos,
        });
        trackingData.rLabel.focus();
        trackingData.rLabel.addTo(assisGroup);
      } else {
        trackingData.rLabel.update(labelText, labelPos);
      }
      ctx2.viewer.requestRender();
    } else if (currentStatus === State.DRAWELLIPSE) {
      if (trackingData.ellipse) {
        const ellipseCurve = new EllipseCurve(
          startPoint.x,
          startPoint.y, // centerX, centerY
          xRadius,
          yRadius, // xRadius, yRadius
          0,
          Math.PI * 2, // startAngle, endAngle
          false, // clockwise
          angle, // rotation
        );
        const geometry = new Geometry().setFromPoints(
          ellipseCurve.getPoints(100),
        );
        const vertices = geometry.vertices;
        trackingData.ellipse.update({ vertexs: vertices });
      } else {
        const ellipseCurve = new EllipseCurve(
          startPoint.x,
          startPoint.y, // centerX, centerY
          xRadius,
          yRadius, // xRadius, yRadius
          0,
          Math.PI * 2, // startAngle, endAngle
          false, // clockwise
          angle, // rotation
        );
        const geometry = new Geometry().setFromPoints(
          ellipseCurve.getPoints(100),
        );
        const vertices = geometry.vertices;
        trackingData.ellipse = new CSLine({
          vertexs: vertices,
          dashed: false,
        });
        assisGroup.add(trackingData.ellipse);
      }

      // 半径辅助线
      if (!trackingData.rLine) {
        trackingData.rLine = new CSLine({
          vertexs: [startPoint, endPoint],
          dashed: true,
          lineWidth: 2,
          depthTest: true,
          csys: faceCSys,
        });
        trackingData.rLine.addTo(assisGroup);
      } else {
        trackingData.rLine.update({
          vertexs: [startPoint, endPoint],
        });
      }

      // 标注
      const labelText = startPoint.distanceTo(endPoint);
      const labelPos = new Vector3();
      labelPos.x = (startPoint.x + endPoint.x) / 2;
      labelPos.y = (startPoint.y + endPoint.y) / 2;
      labelPos.z = (startPoint.z + endPoint.z) / 2;
      if (!trackingData.rLabel) {
        trackingData.rLabel = new css2dInputLable({
          text: labelText,
          position: labelPos,
        });
        trackingData.rLabel.focus();
        trackingData.rLabel.addTo(assisGroup);
      } else {
        trackingData.rLabel.update(labelText, labelPos);
      }

      ctx2.viewer.requestRender();
    }
  };
  return (
    <CommandHint
      status={CommandHintStatus.INFO}
      content={hits}
    />
  );
}

export const cmdEllipse: BaseCommand = {
  id: 'CmdEllipse',
  label: 'command.ellipse',
  icon: 'commands/feature/ellipse',
  create: (label: string, onClose: () => void) => (
    <CircleCommand
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
